#include "VirusEnemy.h"

// Initialize the Virus instance
VirusEnemy* VirusEnemy::m_VirusEnInstance = 0;

VirusEnemy::VirusEnemy(void)
{
}

VirusEnemy::~VirusEnemy(void)
{
}

VirusEnemy* VirusEnemy::GetInstance(void)
{
	// Create the instance if it doesn't exist
	if(!m_VirusEnInstance)
		m_VirusEnInstance = new VirusEnemy;

	// Return the singleton
	return m_VirusEnInstance;
}

void VirusEnemy::DeleteVirusInstance(void)
{
	// Delete all the mines
	VirusEnemy::ShutdownMines();
	// If instance exist, delete it!
	if(m_VirusEnInstance)
	{
		delete m_VirusEnInstance;
		m_VirusEnInstance = NULL;
	}
}

void VirusEnemy::InitMines(IDirect3DDevice9* m_pD3DDevice)
{
	// Get the device
	m_Device = m_pD3DDevice;

	////////////////////////////////////////////////////////////////////////////////
	// X file that is used for all viruses
	// Start .x loading
	LPD3DXBUFFER p_D3DXMtrlBuffer;
	m_dwNumMaterials = 0;

	// Load .x file
	D3DXLoadMeshFromX( "Models\\Mine.X", D3DXMESH_SYSTEMMEM,
		m_Device, NULL, &p_D3DXMtrlBuffer, NULL, &m_dwNumMaterials,
		&m_pMineMesh);

	D3DXMATERIAL* d3dxMaterials =
		(D3DXMATERIAL*)p_D3DXMtrlBuffer->GetBufferPointer();

	// Create an array based on the amount of materials we have
	m_pMaterials = new D3DMATERIAL9[m_dwNumMaterials];
	m_pMeshTextures = new LPDIRECT3DTEXTURE9[m_dwNumMaterials];

	// Load each material
	for( DWORD i = 0; i < m_dwNumMaterials; i++)
	{
		m_pMaterials[i] = d3dxMaterials[i].MatD3D;
		
		m_pMaterials[i].Ambient = m_pMaterials[i].Diffuse;

		m_pMeshTextures[i] = NULL;
		if( d3dxMaterials[i].pTextureFilename)
			D3DXCreateTextureFromFile( m_Device,
				d3dxMaterials[i].pTextureFilename,
				&m_pMeshTextures[i]);
	}
	// End .x loading
	////////////////////////////////////////////////////////////////////////////////

	// Get the instances
	m_ModelMan = ModelManager::GetInstance();
	m_pTimerBar = TimerBar::GetInstance();
	m_pCam = Camera::GetInstance();

	// Set the speed that will be the same for all virus
	fSpeed = 1.0f;
	bExplosion = false;

	// Create the mesh for the sensor radius
	D3DXCreateBox(m_Device, 8.0f, 2.0f, 8.0f, &m_SensorMesh, NULL);
}

void VirusEnemy::CreateMines(short sMinesCount)
{
	// Seed the randomizer
	srand((int)timeGetTime());

	// Create a temporary point so we can grab Obstacle Information a little later in function
	ObstacleManager* obstacleMan = ObstacleManager::GetInstance();
	int iIndex; // variable to load obstacle indexes into so we know how many items we need to check against

	// Get the position of the transition platform
	D3DXVECTOR3 &vecModelPos = m_ModelMan->GetModelPos(3);

	// Set the count to the amount sent in
	sMineCount = sMinesCount;
	fZModelPos = vecModelPos.z;

	// Boundaries for where they can be created
	sXMin = (short)m_pCam->fXNegBound;
	sXMax = (short)m_pCam->fXBound * 2;

	for(short i = 0; i < sMineCount; i++)
	{
		// Create the current spawner structure we are loading
		s_Mines* currentMine = new s_Mines;

		currentMine->bActive = true;

		// Set the positions for the spawner
		currentMine->vecPos.x = (float)(rand() % sXMax + sXMin);
		currentMine->vecPos.y = 10.0f;
		currentMine->vecPos.z = (float)(rand() % (int)vecModelPos.z + (int)vecModelPos.z);

		// Make sure that none of the next spawner are on top of another
		if(i > 0)
		{
			for(int j = 0; j < i; j++)
			{
				if(currentMine->vecPos.x == mineList[j]->vecPos.x)
				{
					currentMine->vecPos.x = (float)(rand() % sXMax + sXMin);
				}
			}
		}
		iIndex = obstacleMan->ReturnFirewallIndex();
		if(iIndex > 0)
		{
			for(int j = 0; j < iIndex; j++)
			{
				if(currentMine->vecPos.x == obstacleMan->firewallList[j]->vecPos.x)
				{
					currentMine->vecPos.x = (float)(rand() % sXMax + sXMin);
					currentMine->vecPos.x += 1.0f;
				}
			}
		}

		/////////////////////////////////////////////////////////////////////////////////////
		// MAGNET BOUNDING "BOX"
		// Calculate and create the bounding boxes
		HRESULT hr = 0;
		BYTE* pVertices = 0;
		if( SUCCEEDED(m_SensorMesh->LockVertexBuffer(0, (void**)&pVertices)))
		{
			// Locking the vertex buffer so we can compute the bounds
			hr = D3DXComputeBoundingBox((D3DXVECTOR3*)pVertices, m_SensorMesh->GetNumVertices(),
				D3DXGetFVFVertexSize(m_SensorMesh->GetFVF()), &currentMine->vecMagBBMin, 
				&currentMine->vecMagBBMax);

			// Unlock the vertex buffer
			m_SensorMesh->UnlockVertexBuffer();

			// With the min and max vectors we can get the 8 corners for the bounding box
			currentMine->vecMagModelBounds[0] = D3DXVECTOR3(currentMine->vecMagBBMin.x, currentMine->vecMagBBMin.y, 
				currentMine->vecMagBBMin.z);
			currentMine->vecMagModelBounds[1] = D3DXVECTOR3(currentMine->vecMagBBMax.x, currentMine->vecMagBBMin.y, 
				currentMine->vecMagBBMin.z);
			currentMine->vecMagModelBounds[2] = D3DXVECTOR3(currentMine->vecMagBBMin.x, currentMine->vecMagBBMax.y, 
				currentMine->vecMagBBMin.z);
			currentMine->vecMagModelBounds[3] = D3DXVECTOR3(currentMine->vecMagBBMax.x, currentMine->vecMagBBMax.y, 
				currentMine->vecMagBBMin.z);
			currentMine->vecMagModelBounds[4] = D3DXVECTOR3(currentMine->vecMagBBMin.x, currentMine->vecMagBBMin.y, 
				currentMine->vecMagBBMax.z);
			currentMine->vecMagModelBounds[5] = D3DXVECTOR3(currentMine->vecMagBBMax.x, currentMine->vecMagBBMin.y, 
				currentMine->vecMagBBMax.z);
			currentMine->vecMagModelBounds[6] = D3DXVECTOR3(currentMine->vecMagBBMin.x, currentMine->vecMagBBMax.y, 
				currentMine->vecMagBBMax.z);
			currentMine->vecMagModelBounds[7] = D3DXVECTOR3(currentMine->vecMagBBMax.x, currentMine->vecMagBBMax.y, 
				currentMine->vecMagBBMax.z);
		}
		/////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////
		// NORMAL BOUNDING "BOX"
		// Calculate and create the bounding boxes
		if( SUCCEEDED(m_pMineMesh->LockVertexBuffer(0, (void**)&pVertices)))
		{
			// Locking the vertex buffer so we can compute the bounds
			hr = D3DXComputeBoundingBox((D3DXVECTOR3*)pVertices, m_pMineMesh->GetNumVertices(),
				D3DXGetFVFVertexSize(m_pMineMesh->GetFVF()), &currentMine->vecBBMin, 
				&currentMine->vecBBMax);

			// Unlock the vertex buffer
			m_pMineMesh->UnlockVertexBuffer();

			// With the min and max vectors we can get the 8 corners for the bounding box
			currentMine->vecModelBounds[0] = D3DXVECTOR3(currentMine->vecBBMin.x, currentMine->vecBBMin.y, 
				currentMine->vecBBMin.z);
			currentMine->vecModelBounds[1] = D3DXVECTOR3(currentMine->vecBBMax.x, currentMine->vecBBMin.y, 
				currentMine->vecBBMin.z);
			currentMine->vecModelBounds[2] = D3DXVECTOR3(currentMine->vecBBMin.x, currentMine->vecBBMax.y, 
				currentMine->vecBBMin.z);
			currentMine->vecModelBounds[3] = D3DXVECTOR3(currentMine->vecBBMax.x, currentMine->vecBBMax.y, 
				currentMine->vecBBMin.z);
			currentMine->vecModelBounds[4] = D3DXVECTOR3(currentMine->vecBBMin.x, currentMine->vecBBMin.y, 
				currentMine->vecBBMax.z);
			currentMine->vecModelBounds[5] = D3DXVECTOR3(currentMine->vecBBMax.x, currentMine->vecBBMin.y, 
				currentMine->vecBBMax.z);
			currentMine->vecModelBounds[6] = D3DXVECTOR3(currentMine->vecBBMin.x, currentMine->vecBBMax.y, 
				currentMine->vecBBMax.z);
			currentMine->vecModelBounds[7] = D3DXVECTOR3(currentMine->vecBBMax.x, currentMine->vecBBMax.y, 
				currentMine->vecBBMax.z);
		}
		// get attributes to draw bounding box
		float width, height, depth;
		/*width = (currentMine->vecBBMax.x) - (currentMine->vecBBMin.x);
		height = (currentMine->vecBBMax.y) - (currentMine->vecBBMin.y);
		depth = (currentMine->vecBBMax.z) - (currentMine->vecBBMin.z);*/

		//// Testing for magnet box
		width = (currentMine->vecMagBBMax.x) - (currentMine->vecMagBBMin.x);
		height = (currentMine->vecMagBBMax.y) - (currentMine->vecMagBBMin.y);
		depth = (currentMine->vecMagBBMax.z) - (currentMine->vecMagBBMin.z);

		// Make a mesh
		D3DXCreateBox( m_Device, width, height, depth, &m_BBMesh, NULL);

		// Add the currently loaded spawner to the list
		mineList.push_back(currentMine);
	}
}

void VirusEnemy::UpdateMines(float deltaTime)
{
	for(int i = 0; i < sMineCount; i++)
	{
		if(mineList[i]->bActive)
		{
			if(SensorCollisionDetection(i))
			{
				if(!CollisionDetection(i))
					StartPull(i, deltaTime);
				else
				{
					// Mine collision with player occured
					mineList[i]->bActive = false;
					bExplosion = true;
					m_pTimerBar->DamageTaken();
				}
			}
		}
	}
}

void VirusEnemy::StartPull(short sMineID, float fDeltaTime)
{
	// Calculate the distance between the player and virus
	vecDistance.x = m_ModelMan->modelList[0]->vecPos.x - mineList[sMineID]->vecPos.x;
	vecDistance.y = m_ModelMan->modelList[0]->vecPos.y - mineList[sMineID]->vecPos.y;
	vecDistance.z = m_ModelMan->modelList[0]->vecPos.z - mineList[sMineID]->vecPos.z;

	// Start adding the direction vector to the virus to move
	// it towards the player
	mineList[sMineID]->vecPos.x += vecDistance.x * fSpeed * fDeltaTime;
	mineList[sMineID]->vecPos.y += vecDistance.y * fSpeed * fDeltaTime;
	mineList[sMineID]->vecPos.z += vecDistance.z * fSpeed * fDeltaTime;
}

void VirusEnemy::RenderMines(void)
{
	for(int i = 0; i < sMineCount; i++)
	{
		if(mineList[i]->bActive)
		{
		// Reset the matrices
		D3DXMatrixIdentity(&mineList[i]->scaleMat);
		D3DXMatrixIdentity(&mineList[i]->rotMat);
		D3DXMatrixIdentity(&mineList[i]->transMat);
		D3DXMatrixIdentity(&mineList[i]->worldMat);

		// Rotate, scale, and move the power up
		D3DXMatrixScaling(&mineList[i]->scaleMat, 1.0f, 1.0f, 1.0f);
		D3DXMatrixRotationYawPitchRoll(&mineList[i]->rotMat, 0.0f, 0.0f, 0.0f);
		D3DXMatrixTranslation(&mineList[i]->transMat, mineList[i]->vecPos.x, mineList[i]->vecPos.y, mineList[i]->vecPos.z);

		// Scale & rotation * translation
		D3DXMatrixMultiply(&mineList[i]->scaleMat, &mineList[i]->rotMat, &mineList[i]->scaleMat);
		D3DXMatrixMultiply(&mineList[i]->worldMat, &mineList[i]->scaleMat, &mineList[i]->transMat);

		// Set the transform
		m_Device->SetTransform(D3DTS_WORLD, &mineList[i]->worldMat);

		// Start .x drawing
		for( DWORD j = 0; j < m_dwNumMaterials; j++)
		{
			m_Device->SetMaterial( &m_pMaterials[j]);
			m_Device->SetTexture( 0, m_pMeshTextures[j]);

			m_pMineMesh->DrawSubset(j);
		}

		m_Device->SetTexture(0, NULL);

		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//		Testing code!!!!      Drawing Bounding Box to ensure it is correcly placed on model
		//// Turning on Wireframe
		//m_Device->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );

		//// Draw box
		//m_BBMesh->DrawSubset(0);

		//// Turn off WireFrame
		//m_Device->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		}
	}
}

void VirusEnemy::ReactivateMines(void)
{
	for(int i = 0; i < sMineCount; i++)
	{
		mineList[i]->bActive = true;
		// Set the positions for the spawner
		mineList[i]->vecPos.x = (float)(rand() % 16 - 8);
		mineList[i]->vecPos.y = 10.0f;
		mineList[i]->vecPos.z = (float)(rand() % 16 - 2) + fZModelPos;
	}
}

void VirusEnemy::CalculateWorldBB(short sMineID)
{
	if(mineList[sMineID]->bActive)
	{
		// Convert Object's model space bounding box to world space
		// Transform the 8 corners of our object space bounding box into world space
		D3DXVECTOR3 worldBounds[8];
		for( int i = 0; i < 8; i++)
			D3DXVec3TransformCoord( &worldBounds[i], &mineList[sMineID]->vecModelBounds[i], &mineList[sMineID]->worldMat );

		// Pick a corner and set the smallest point and largest point to that corner
		mineList[sMineID]->worldBBmin = worldBounds[0];
		mineList[sMineID]->worldBBmax = worldBounds[0];

		// Find the new minimum and maximum points after transformation
		// Now, compare the other corners and see if they are smaller or larger
		for( short i = 1; i < 8; i++ )
		{
			if(worldBounds[i].x < mineList[sMineID]->worldBBmin.x)
				mineList[sMineID]->worldBBmin.x = worldBounds[i].x;
			if(worldBounds[i].x > mineList[sMineID]->worldBBmax.x)
				mineList[sMineID]->worldBBmax.x = worldBounds[i].x;
			if(worldBounds[i].y < mineList[sMineID]->worldBBmin.y)
				mineList[sMineID]->worldBBmin.y = worldBounds[i].y;
			if(worldBounds[i].y > mineList[sMineID]->worldBBmax.y)
				mineList[sMineID]->worldBBmax.y = worldBounds[i].y;
			if(worldBounds[i].z < mineList[sMineID]->worldBBmin.z)
				mineList[sMineID]->worldBBmin.z = worldBounds[i].z;
			if(worldBounds[i].z > mineList[sMineID]->worldBBmax.z)
				mineList[sMineID]->worldBBmax.z = worldBounds[i].z;	
		}
	}
}

void VirusEnemy::CalculateSensorWorldBB(short sMineID)
{
	// Convert Object's model space bounding box to world space
	// Transform the 8 corners of our object space bounding box into world space
	D3DXVECTOR3 worldBounds[8];
	for( int i = 0; i < 8; i++)
		D3DXVec3TransformCoord( &worldBounds[i], &mineList[sMineID]->vecMagModelBounds[i], &mineList[sMineID]->worldMat );

	// Pick a corner and set the smallest point and largest point to that corner
	mineList[sMineID]->worldMagBBmin = worldBounds[0];
	mineList[sMineID]->worldMagBBmax = worldBounds[0];

	// Find the new minimum and maximum points after transformation
	// Now, compare the other corners and see if they are smaller or larger
	for( short i = 1; i < 8; i++ )
	{
		if(worldBounds[i].x < mineList[sMineID]->worldMagBBmin.x)
			mineList[sMineID]->worldMagBBmin.x = worldBounds[i].x;
		if(worldBounds[i].x > mineList[sMineID]->worldMagBBmax.x)
			mineList[sMineID]->worldMagBBmax.x = worldBounds[i].x;
		if(worldBounds[i].y < mineList[sMineID]->worldMagBBmin.y)
			mineList[sMineID]->worldMagBBmin.y = worldBounds[i].y;
		if(worldBounds[i].y > mineList[sMineID]->worldMagBBmax.y)
			mineList[sMineID]->worldMagBBmax.y = worldBounds[i].y;
		if(worldBounds[i].z < mineList[sMineID]->worldMagBBmin.z)
			mineList[sMineID]->worldMagBBmin.z = worldBounds[i].z;
		if(worldBounds[i].z > mineList[sMineID]->worldMagBBmax.z)
			mineList[sMineID]->worldMagBBmax.z = worldBounds[i].z;	
	}
}

bool VirusEnemy::CollisionDetection(short sMineID)
{
	if(mineList[sMineID]->bActive)
	{
		// Calculate the world space bounding box for player object
		m_ModelMan->CalculateWorldBB(0);

		// Calculate the world space bounding box for power up object
		CalculateWorldBB(sMineID);

		// Collision check on the boundaries
		if(m_ModelMan->modelList[0]->worldBBmax.x < mineList[sMineID]->worldBBmin.x)
			return false;
		if(m_ModelMan->modelList[0]->worldBBmax.y < mineList[sMineID]->worldBBmin.y)
			return false;
		if(m_ModelMan->modelList[0]->worldBBmax.z < mineList[sMineID]->worldBBmin.z)
			return false;
		if(m_ModelMan->modelList[0]->worldBBmin.x > mineList[sMineID]->worldBBmax.x)
			return false;
		if(m_ModelMan->modelList[0]->worldBBmin.y > mineList[sMineID]->worldBBmax.y)
			return false;
		if(m_ModelMan->modelList[0]->worldBBmin.z > mineList[sMineID]->worldBBmax.z)
			return false;

		// Collision occured
		mineList[sMineID]->bActive = false;
		return true;
	}
	// No collision occured
	return false;
}

bool VirusEnemy::SensorCollisionDetection(short sMineID)
{
	// Calculate the world space bounding box for player object
	m_ModelMan->CalculateWorldBB(0);

	// Calculate the world space bounding box for power up object
	CalculateSensorWorldBB(sMineID);

	// Collision check on the boundaries
	if(m_ModelMan->modelList[0]->worldBBmax.x < mineList[sMineID]->worldMagBBmin.x)
		return false;
	if(m_ModelMan->modelList[0]->worldBBmax.y < mineList[sMineID]->worldMagBBmin.y)
		return false;
	if(m_ModelMan->modelList[0]->worldBBmax.z < mineList[sMineID]->worldMagBBmin.z)
		return false;
	if(m_ModelMan->modelList[0]->worldBBmin.x > mineList[sMineID]->worldMagBBmax.x)
		return false;
	if(m_ModelMan->modelList[0]->worldBBmin.y > mineList[sMineID]->worldMagBBmax.y)
		return false;
	if(m_ModelMan->modelList[0]->worldBBmin.z > mineList[sMineID]->worldMagBBmax.z)
		return false;

	// Collision occured
	return true;
}

void VirusEnemy::ClearMineList(void)
{
	// Clear the spawner list
	for(int i = sMineCount - 1; i >= 0; i--)
	{
		delete mineList[i];
		mineList[i] = NULL;
		mineList.pop_back();
	}
	sMineCount = 0;
}

void VirusEnemy::ShutdownMines(void)
{
	// Delete the power up list
	for(int i = 0; i < sMineCount; i++)
	{
		delete mineList[i];
		mineList[i] = NULL;
	}

	// Reset the power up count to 0
	sMineCount = 0;

	if(m_SensorMesh)
	{
		m_SensorMesh->Release();
		m_SensorMesh = NULL;
	}

	/////////////////////////////////////////////////////////////////////////////
	// .X file deleting
	if(m_pMaterials != NULL)
	{
		delete m_pMaterials;
	}

	if(m_pMeshTextures)
	{
		for(DWORD j = 0; j < m_dwNumMaterials; j++)
		{
			if(m_pMeshTextures[j])
			{
				m_pMeshTextures[j]->Release();
				m_pMeshTextures[j] = NULL;
			}
		}

		delete[] m_pMeshTextures;
	}

	if(m_pMineMesh)
	{
		m_pMineMesh->Release();
		m_pMineMesh = NULL;
	}
	/////////////////////////////////////////////////////////////////////////////
}
